#include "ssl.h"
#include "log.h"

#ifdef _WIN32
#include <windows.h>
static void* dlopen(const char* dll_name, int flags) { return LoadLibraryA(dll_name); }
static void* dlsym(void* handle, const char* func_name) { return (void*)GetProcAddress((HMODULE)handle, func_name); }
#else
#include <unistd.h>
#define Sleep(x) sleep(x/1000)
#include <dlfcn.h>
#endif

void (*SSL::SSL_free)(ssl_st*) = 0;
int (*SSL::SSL_accept)(ssl_st*) = 0;
int (*SSL::SSL_connect)(ssl_st*) = 0;
int (*SSL::SSL_read)(ssl_st*, void *, int) = 0;
int (*SSL::SSL_write)(ssl_st*, const void *, int) = 0;
int (*SSL::SSL_get_error)(ssl_st*, int) = 0;
int (*SSL::SSL_set_fd)(ssl_st*, int) = 0;
ssl_st* (*SSL::SSL_new)(ssl_ctx_st*) = 0;
ssl_ctx_st* (*SSL::SSL_CTX_new)(ssl_method_st*) = 0;
ssl_method_st* (*SSL::SSLv23_server_method)() = 0;
int* (*SSL::SSL_library_init)() = 0;
int (*SSL::SSL_CTX_use_certificate_file)(ssl_ctx_st*, const char *, int) = 0;
int (*SSL::SSL_CTX_use_PrivateKey_file)(ssl_ctx_st*, const char *, int) = 0;
void (*SSL::SSL_CTX_free)(ssl_ctx_st*) = 0;
int (*SSL::CRYPTO_num_locks)() = 0;
void (*SSL::CRYPTO_set_locking_callback)(void (*)(int, int, const char *, int)) = 0;
void (*SSL::CRYPTO_set_id_callback)(unsigned long (*)(void)) = 0;
const char* (*SSL::SSL_get_cipher_list)(const ssl_st* ssl, int priority) = 0;


SSL::SSL()
{
  if (SSL_library_init == 0)
  {
#ifdef _WIN32
    void* handle = dlopen("ssleay32.dll", 0);
#else
    void* handle = dlopen("libssl.so", RTLD_LAZY);
#endif
    Log::ErrorIf(handle == 0, "Loading SSL library failed");

    SSL_free = (void (*)(ssl_st*))dlsym(handle, "SSL_free");
    SSL_accept = (int (*)(ssl_st*))dlsym(handle, "SSL_accept");
    SSL_connect = (int (*)(ssl_st*))dlsym(handle, "SSL_connect");
    SSL_read = (int (*)(ssl_st*, void *, int))dlsym(handle, "SSL_read");
    SSL_write = (int (*)(ssl_st*, const void *, int))dlsym(handle, "SSL_write");
    SSL_get_error = (int (*)(ssl_st*, int))dlsym(handle, "SSL_get_error");
    SSL_set_fd = (int (*)(ssl_st*, int))dlsym(handle, "SSL_set_fd");
    SSL_new = (ssl_st* (*)(ssl_ctx_st*))dlsym(handle, "SSL_new");
    SSL_CTX_new = (ssl_ctx_st* (*)(ssl_method_st*))dlsym(handle, "SSL_CTX_new");
    SSLv23_server_method = (ssl_method_st* (*)())dlsym(handle, "SSLv23_server_method");
    SSL_library_init = (int* (*)())dlsym(handle, "SSL_library_init");
    SSL_CTX_use_certificate_file = (int (*)(ssl_ctx_st*, const char *, int))dlsym(handle, "SSL_CTX_use_certificate_file");
    SSL_CTX_use_PrivateKey_file = (int (*)(ssl_ctx_st*, const char *, int))dlsym(handle, "SSL_CTX_use_PrivateKey_file");
    SSL_CTX_free = (void (*)(ssl_ctx_st*))dlsym(handle, "SSL_CTX_free");
    SSL_get_cipher_list = (const char* (*)(const ssl_st*, int))dlsym(handle, "SSL_get_cipher_list");

    (*SSL_library_init)();
  }
  ssl_method_st* method = (*SSLv23_server_method)();
  context = (*SSL_CTX_new)(method);
  int result = (*SSL_CTX_use_certificate_file)(context, "../../data/ssl_cert.pem", 1/*SSL_FILETYPE_PEM*/);
  Log::ErrorIf(result == 0, "SSL_CTX_use_certificate_file failed.");
  result = (*SSL_CTX_use_PrivateKey_file)(context, "../../data/ssl_cert.pem", 1/*SSL_FILETYPE_PEM*/);
  Log::ErrorIf(result == 0, "SSL_CTX_use_PrivateKey_file failed.");
}

SSL::~SSL()
{
  (*SSL_CTX_free)(context);
}

void SSL::SetSocket(int socket)
{
  st = (*SSL_new)(context);
  int result = (*SSL_set_fd)(st, socket);
  Log::ErrorIf(result != 1, "SSL_set_fd failed.");
  Sleep(100);
  result = (*SSL_accept)(st);
  Log::ErrorIf(result != 1, "SSL_accept failed.");
}

int SSL::Read(char* buf, int len)
{
  return (*SSL_read)(st, buf, len);
}

int SSL::Write(const char* buf, int len)
{
  return (*SSL_write)(st, buf, len);
}

